package com.synthefact.daj.model.threaded.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.synthefact.daj.model.ChannelIf;
import com.synthefact.daj.model.NetworkIf;
import com.synthefact.daj.model.ProgramIf;
import com.synthefact.daj.model.threaded.ThreadedNodeIf;

/**
 * A node is a component in the network.
 * <p>
 * Copyright (C) 2011 Synthefact
 * <p>
 * Based on code from (c) 1997, Wolfgang Schreiner <Wolfgang.Schreiner@risc.uni-linz.ac.at>
 * http://www.risc.uni-linz.ac.at/software/daj
 */
public class Node extends Thread implements ThreadedNodeIf {

    final Logger logger = LoggerFactory.getLogger(Node.class);

    /** This node is part of a network */
    private NetworkIf iNetwork;

    /** It has a set of input channels, */
    private Set<ChannelIf> iInChannels;
    /** and a set of output channels */
    private Set<ChannelIf> iOutChannels;

    /** This node has a program that it executes */
    private ProgramIf iProgram;

    /** This node counts context switches */
    private int iCntSwitches;

    private Thread iNodeThread;

    // private boolean iTerminatable = false;
    //
    // private boolean iContinuable = true;

    private boolean iActive = false;

    private boolean iReadyToEnd = false;

    /**
     * Construct a node in the given network, to run the given program.
     * 
     * @param program
     *            the program to run.
     * @param network
     *            the network for this node.
     */
    public Node(ProgramIf program, NetworkIf network) {
	iProgram = program;
	iNetwork = network;
	iInChannels = new HashSet<ChannelIf>();
	iOutChannels = new HashSet<ChannelIf>();
	iNodeThread = Thread.currentThread();
    }

    @Override
    public NetworkIf getNetwork() {
	return iNetwork;
    }

    /**
     * Increment the number of context switches
     */
    public void incSwitches() {
	iCntSwitches++;
    }

    /**
     * Get the number of context switches
     */
    public int getSwitches() {
	return iCntSwitches;
    }

    /**
     * On node start, execute the program for this node and then terminate.
     */
    synchronized public void run() {
	logger.debug(String.format("Running node with program %s", iProgram));
	try {
	    wait(); // wait for notify from network
	} catch (InterruptedException e) {
	    logger.error("InterruptedException");
	    throw new RuntimeException(e);
	}
	iProgram.main();
    }

    /**
     * Add a channel to the set of input channels
     */
    public void addInChannel(ChannelIf channel) {
	iInChannels.add(channel);
    }

    /**
     * Add a channel to the set of output channels
     */
    public void outChannel(Channel channel) {
	iOutChannels.add(channel);
    }

    @Override
    public ChannelIf getInChannel(int inChannelIndex) {
	List<ChannelIf> listOfInChannels = new ArrayList<ChannelIf>(iInChannels);
	return listOfInChannels.get(inChannelIndex);
    }

    @Override
    public Set<ChannelIf> getInChannelSet() {
	return iInChannels;
    }

    @Override
    public ChannelIf getOutChannel(int outChannelIndex) {
	List<ChannelIf> listOfInChannels = new ArrayList<ChannelIf>(iOutChannels);
	return listOfInChannels.get(outChannelIndex);
    }

    @Override
    public Set<ChannelIf> getOutChannelSet() {
	return iOutChannels;
    }

    @Override
    public ProgramIf getProgram() {
	return iProgram;
    }

    @Override
    public ChannelIf selectInChannel() {
	for (ChannelIf channel : iInChannels) {
	    if (channel.isReady()) {
		return channel;
	    }
	}
	return null;
    }

    @Override
    public void addOutChannel(ChannelIf channel) {
	iOutChannels.add(channel);
    }

    @Override
    public boolean isActive() {
	return iActive;
    }

    @Override
    public void setActive(boolean active) {
	iActive = active;

    }

    // @Override
    // public boolean isContinuable() {
    // return iContinuable;
    // }
    //
    // @Override
    // public void setContinue(boolean continuable) {
    // iContinuable = continuable;
    // }
    //
    // @Override
    // public boolean isTerminatable() {
    // return iTerminatable;
    // }
    //
    // @Override
    // public void setTerminate(boolean terminate) {
    // iTerminatable = terminate;
    // }

    @Override
    public Thread getThread() {
	return iNodeThread;
    }

    @Override
    public void setReadyToEnd() {
	iReadyToEnd = true;
    }

    @Override
    public boolean isReadyToEnd() {
	return iReadyToEnd;
    }

    public String toString() {
	return String.format("This is Node for program: %s", iProgram);
    }

}
